﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Cofe.Core.Utils;
using System.Threading.Tasks;
using Cofe.Core.Dynamic;

namespace Cofe.Core.Property
{
    public static class IPropertyHostExtension
    {
        #region Property

        ///// <summary>
        ///// Get all supported properties and actions, if filters != null, the property must also match any or all of the specified filters.
        ///// </summary>
        ///// <typeparam name="T"></typeparam>
        ///// <param name="implementer"></param>
        ///// <param name="matchMode"></param>
        ///// <param name="filters"></param>
        ///// <returns></returns>
        //public static object[] GetSupportedProperty<T>(
        //    this IPropertyHost<T> implementer,
        //    MatchMode matchMode = MatchMode.All,
        //    params IPropertyFilter[] filters)
        //{
        //    return implementer.GetSupportedProperty(
        //        PropertyRetrivalOptions.NotApplied, matchMode, filters);
        //}

        /// <summary>
        /// Try to get a property, and use default value if any exception.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="implementer"></param>
        /// <param name="property"></param>
        /// <param name="defaultValue"></param>
        /// <param name="options"></param>
        /// <returns></returns>
        public async static Task<PropertyPair> TryGetPropertyAsync<V>(this IPropertyHost implementer,
            object property, V defaultValue)
        {
            try
            {
                return await implementer.Behaviors.GetPropertyAsync(property);
            }
            catch
            {
                return PropertyPair.FromValue(property, defaultValue);
            }
        }

        public static PropertyPair TryGetProperty<V>(this IPropertyHost implementer,
           object property, V defaultValue)
        {
            return TryGetPropertyAsync(implementer, property, defaultValue).Result;

        }

        public static V TryGetValue<V>(this IPropertyHost implementer,
          object property, V defaultValue)
        {
            return TryGetPropertyAsync(implementer, property, defaultValue).Result.ValueAs<V>();
        }


        #endregion

        #region PropertyString

        /// <summary>
        /// Is the specific Metadata or resource property supported.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="implementer"></param>
        /// <param name="propertyString"></param>
        /// <returns></returns>
        public static bool IsPropertySupported(this IPropertyHost implementer,
            string propertyString)
        {
            return implementer.GetSupportedPropertyString()
                .Any((ps) =>
                {
                    return ps.Equals(propertyString,
                        StringComparison.CurrentCultureIgnoreCase);
                });
        }

        /// <summary>
        /// Is the specific property is Metadata 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="implementer"></param>
        /// <param name="propertyString"></param>
        /// <returns></returns>
        public static bool HasMetadataPropertyNamed(this IPropertyHost implementer,
            string propertyString)
        {
            return implementer.GetSupportedPropertyString(true, false)
               .Any((ps) => { return ps.Equals(propertyString, StringComparison.CurrentCultureIgnoreCase); });
        }

        /// <summary>
        /// Is the specific property is Resource
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="implementer"></param>
        /// <param name="propertyString"></param>
        /// <returns></returns>
        public static bool HasResourcePropertyNamed(this IPropertyHost implementer,
            string propertyString)
        {
            return implementer.GetSupportedPropertyString(false, true)
                .Any((ps) => { return ps.Equals(propertyString, StringComparison.CurrentCultureIgnoreCase); });
        }

        /// <summary>
        /// Get a list of resources marked as Metadata or Resource.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="implementer"></param>
        /// <param name="metadata"></param>
        /// <param name="resource"></param>
        /// <returns></returns>
        public static string[] GetSupportedPropertyString(this IPropertyHost implementer,
            bool metadata = true, bool resource = true, bool action = false)
        {
            return implementer.Behaviors.GetSupportedProperties()
                .Where((p) =>
                {
                    return (metadata && DecoratorExtension.IsMetadata(p)) ||
                        (resource && DecoratorExtension.IsWebResource(p)) ||
                        (action && DecoratorExtension.IsAction(p));
                })
                .Select((p) => { return EnumUtils.GetResourceName(p); })
                .ToArray();
        }

        /// <summary>
        /// Get a metadata or resource property.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="implementer"></param>
        /// <param name="propertyString"></param>
        /// <param name="options"></param>
        /// <returns></returns>
        [Obsolete]
        public static Task<PropertyPair> GetPropertyAsync(this IPropertyHost implementer,
            string propertyString)
        {
            object property = implementer.Behaviors.GetSupportedProperties()
                .Where((p) => { return DecoratorExtension.IsMetadata(p) || DecoratorExtension.IsWebResource(p) || DecoratorExtension.IsAction(p); })
                .Where((p) => { return EnumUtils.GetResourceName(p).Equals(propertyString, StringComparison.CurrentCultureIgnoreCase); })
                .FirstOrDefault();
            if (property != null)
                return implementer.Behaviors.GetPropertyAsync(property);

            throw new ArgumentException(propertyString);
        }

        /// <summary>
        /// Get a metadata or resource property.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="implementer"></param>
        /// <param name="propertyString"></param>
        /// <param name="options"></param>
        /// <returns></returns>
        public static async Task<bool> SetPropertyAsync(this IPropertyHost implementer,
            string propertyString,
            object value)
        {
            object property = implementer.Behaviors.GetSupportedProperties()
                .Where((p) => { return DecoratorExtension.IsMetadata(p) || DecoratorExtension.IsWebResource(p); })
                .Where((p) => { return EnumUtils.GetResourceName(p).Equals(propertyString, StringComparison.CurrentCultureIgnoreCase); })
                .FirstOrDefault();
            if (property != null)
                return await implementer.Behaviors.SetPropertyAsync(PropertyPair.FromValue(property, value));

            throw new ArgumentException(propertyString);
        }

        #endregion

        #region Property method.


        public static T PropertyTry<T>(this IPropertyHost implementer, object property)
        {
            if (implementer.Behaviors.IsPropertySupported(property))
                return implementer.Behaviors.GetProperty(property).ValueAs<T>();
            return default(T);
        }

        public static T Property<T>(this IPropertyHost implementer, object property)
        {
            T retVal = PropertyTry<T>(implementer, property);
            if (retVal == null)
                throw new NotSupportedException();
            return retVal;
        }

        #endregion

        public static async Task<StreamContainer> OpenStreamAsync(this IPropertyHost propertyHost, bool read, bool write)
        {
            object prop;
            if (read && write)
                prop = CofeStreamProperties.OpenReadWriteAsync;
            else if (read)
                prop = CofeStreamProperties.OpenReadAsync;
            else if (write)
                prop = CofeStreamProperties.OpenWriteAsync;
            else throw new ArgumentException();

            return (await propertyHost.Behaviors.InvokeAsync<StreamContainer>(prop));
        }

        public static StreamContainer OpenStream(this IPropertyHost propertyHost, bool read, bool write)
        {
            return Task.Run<StreamContainer>(async () => await propertyHost.OpenStreamAsync(read, write)).Result;
        }

        public static void ChangeState(this IPropertyHost[] propertyHosts, PropertyHostState state)
        {
            foreach (var ph in propertyHosts)
                ChangeState(ph, state);
        }

        public static void ChangeState(this IPropertyHost propertyHost, PropertyHostState state)
        {
            propertyHost.State = state;
        }

    }
}
